#pragma once

#include <entt/entt.hpp>
#include "ly/instances.hpp"
#include "ly/object.hpp"
#include "ly/path.hpp"
#include "ly/polygon.hpp"
#include "ly/shapes.hpp"
#include "ly/types.hpp"

namespace ly {
class Layout;
class Cell;
class Instances;

class LY_API Cell : public Object {
 public:
  typedef std::shared_ptr<Cell> sptr;
  typedef std::shared_ptr<Layout> layout_sptr;
  typedef std::weak_ptr<Layout> layout_wptr;
  typedef std::shared_ptr<Shapes> shapes_sptr;
  typedef std::shared_ptr<Instances> instances_sptr;
  typedef std::shared_ptr<Cell> cell_sptr;
  typedef entt::registry reg_t;
  typedef std::map<shape_id_type, shapes_sptr> shapes_map_t;

 public:
  Cell() : m_name("") {};
  Cell(const std::string& name) : m_name(name) {};
  Cell(const std::string& name, layout_wptr parent) : mp_layout(parent) {};
  Cell(cell_id_type id, layout_wptr parent) : m_id(m_id), mp_layout(parent) {};

  ~Cell() {};

  template <typename... Args>
  static cell_sptr create(Args&&... args) {
    return std::make_shared<Cell>(std::forward<Args>(args)...);
  }

  std::string name() { return m_name; }
  std::string get_basic_name() { return m_name; }

  void name(const std::string& name) { m_name = name; }

  void id(cell_id_type id) { m_id = id; }

  cell_id_type id() { return m_id; }
  cell_id_type cell_id() { return m_id; }

  void layout(layout_sptr lyt) { mp_layout = lyt; }
  layout_sptr layout() { return mp_layout.lock(); }

  properties_id_type prop_id() const { return m_prop_id; }
  void prop_id(properties_id_type id);

  void swap(unsigned int i1, unsigned int i2);

  shapes_sptr shapes(shape_id_type id);
  instances_sptr instances();

  void redo(Op* op);
  void undo(Op* op);

 private:
  cell_sptr this_sptr() { return std::dynamic_pointer_cast<Cell>(shared_from_this()); }

 private:
  cell_id_type m_id;
  properties_id_type m_prop_id{0};
  std::string m_name;

  shapes_map_t m_shapes;
  instances_sptr m_insts;

  layout_wptr mp_layout;
};

}  // namespace ly
